//
// @file：UserDefaults+Codable+JW.swift
// @project：JWHelperKit
// @author：linjw(10126121@qq.com)
// @time: 2022/12/20
//  Copyright © 2018年 Linjw. All rights reserved.
//

import Foundation

@propertyWrapper
public struct UserDefault<Value: JWUserDefaultsCanStorageDelegate> {
    let key: String
    let defaultValue: Value
    var userDefaults: UserDefaults
    
    public init(_ key: String, defaultValue: Value, userDefaults: UserDefaults = .standard) {
        self.key = key
        self.defaultValue = defaultValue
        self.userDefaults = userDefaults
    }
    
    public var wrappedValue: Value {
        get {
            return userDefaults.object(forKey: key) as? Value ?? defaultValue
        }
        set {
            userDefaults.set(newValue, forKey: key)
        }
    }
}

@propertyWrapper
public struct UserDefaultsCodable<Value: Codable> {
    
    let key: String
    let defaultValue: Value?
    private var result: Value?
    var userDefaults: UserDefaults
    
    public init(_ key: String, defaultValue: Value? = nil, userDefaults: UserDefaults = .standard) {
        self.key = key
        self.defaultValue = defaultValue
        self.userDefaults = userDefaults
    }
    
    public var wrappedValue: Value? {
        mutating get {
            
            if let temResult = result {
                return temResult
            } else {
                self.result = find() ?? defaultValue
                return self.result
            }
        }
        set {
            result = newValue
            if newValue == nil {
                remove()
            } else {
                save(value: newValue)
            }
        }
    }
    
    @discardableResult
    private func save<T: Codable>(value: T) -> Bool {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        guard let data = try? encoder.encode(value),
            let stringJson = String(data: data, encoding: .utf8) else {
             return false
        }
        userDefaults.set(stringJson, forKey: key)
        return userDefaults.synchronize()
    }
    
    @discardableResult
    private func find() -> Value? {
        
        guard let jsonString = userDefaults.object(forKey: key) as? String,
            let jsonData = jsonString.data(using: .utf8),
              let result = try? JSONDecoder().decode(Value.self, from: jsonData)
            else {
                return nil
        }
        return result
    }
    
    @discardableResult
    private func remove() -> Bool {
        userDefaults.removeObject(forKey: key)
        return userDefaults.synchronize()
    }
}


/// 可在UserDefaults存取的类型协议
public protocol JWUserDefaultsCanStorageDelegate {}

extension Data: JWUserDefaultsCanStorageDelegate {}
extension NSData: JWUserDefaultsCanStorageDelegate {}

extension String: JWUserDefaultsCanStorageDelegate {}
extension NSString: JWUserDefaultsCanStorageDelegate {}

extension Date: JWUserDefaultsCanStorageDelegate {}
extension NSDate: JWUserDefaultsCanStorageDelegate {}

extension NSNumber: JWUserDefaultsCanStorageDelegate {}
extension Bool: JWUserDefaultsCanStorageDelegate {}
extension Int: JWUserDefaultsCanStorageDelegate {}
extension Int8: JWUserDefaultsCanStorageDelegate {}
extension Int16: JWUserDefaultsCanStorageDelegate {}
extension Int32: JWUserDefaultsCanStorageDelegate {}
extension Int64: JWUserDefaultsCanStorageDelegate {}
extension UInt: JWUserDefaultsCanStorageDelegate {}
extension UInt8: JWUserDefaultsCanStorageDelegate {}
extension UInt16: JWUserDefaultsCanStorageDelegate {}
extension UInt32: JWUserDefaultsCanStorageDelegate {}
extension UInt64: JWUserDefaultsCanStorageDelegate {}
extension Double: JWUserDefaultsCanStorageDelegate {}
extension Float: JWUserDefaultsCanStorageDelegate {}
#if os(macOS)
extension Float80: JWUserDefaultsCanStorageDelegate {}
#endif

extension Array: JWUserDefaultsCanStorageDelegate where Element: JWUserDefaultsCanStorageDelegate {}

extension Dictionary: JWUserDefaultsCanStorageDelegate where Key == String, Value: JWUserDefaultsCanStorageDelegate {}
